
#ifndef IIAPPLICATION_H
#define IIAPPLICATION_H

#include "../common.hpp"
#include "iicommon.hpp"
#include "../iapplication.hpp"

#include <map>
#include <utility>
#include <iostream>

#include <QObject>
#include <QApplication>
#include <QWidget>

#ifdef DEBUG
# include <QDebug>
#endif

extern "C" {
#include <tnw++/widgetbase.h>
#include <TinyWidgets/tnWidgets.h>
#include <TinyWidgets/tnTypes.h>
}


////////////
// Colors //
////////////

#include <QColor>
#include <QPalette>

extern "C" {
#include <microwin/mwtypes.h>
}

// Qt colors

class QIColor : public IColor,
		public IMapping<IColor::Color, QString> {

public:
	QIColor();
};

// TNW++ colors

class TIColor : public IColor,
		public IMapping<IColor::Color, unsigned long> {

public:
	TIColor();
};


/////////////
// Widgets //
/////////////

// Qt widget
class QIWidget : virtual public QWidget, virtual public IWidget
{
	Q_OBJECT
	
	QI_WIDGET(virtual, QWidget);

protected:
	QIColor color_mappings;
};

// TNW++ widget
class TIWidget : virtual public IWidget, virtual protected CWidget {

private:
	TIColor color_mappings;

public:
	TIWidget() {
	}

	virtual void show() {
		CWidget::Enable(TRUE);
		CWidget::Show(TRUE);
	}

	virtual void hide() {
		CWidget::Show(FALSE);
	}

	virtual void refresh() {
		CWidget::Refresh();
	}

	virtual void resize(int w, int h)
	{
		TN_WIDGET *wg = **this;
		tnMoveAndResizeWidget(wg, wg->props.posx, wg->props.posy, w, h);
	}

	virtual void move(int x, int y)
	{
		tnMoveAndResizeWidget(THIS_TWG, x, y, THIS_TWG->props.width, THIS_TWG->props.height);
	}
	
	virtual void enable()
	{
		tnSetEnabled(**this, TRUE);
	}

	virtual void disable()
	{
		tnSetEnabled(**this, FALSE);
	}

	virtual void setBgColor(IColor::Color color) {
		TN_WIDGET *wg = **this;
		tnSetWidgetBGColor(wg, color_mappings[color]);
	}

	virtual void setFgColor(IColor::Color color) {
		TN_WIDGET *wg = **this;
		tnSetWidgetFGColor(wg, color_mappings[color]);
	}

	virtual int getWidth() const
	{
		return THIS_TWG->props.width;
	}

	TN_WIDGET* operator*() {
		return *this;
	}
	
};

///////////
// Timer //
///////////

// Qt timers

#include <QTimer>

class IActionHandler;
class QTimerActionHandler;
class QIApplication;

class QITiming : public QTimer, public QIWidget
{
public:	
	virtual void show() { }
};

class QITimer : public ITimer
{
private:
	friend class QTimerActionHandler;
	friend class QIApplication;
	
	static std::map<QObject*, ITimerHandler*> timer_widget_map;
};

class QTimerActionHandler : public IActionHandler
{
public:
	
	void handler(IWidget &action_parent);

};

// TNW timers

class TITimer : public ITimer {

public:
	class_iexception(ITimerNonExistent, "requested timer object does not exist");
	
	static void generic_callback(TN_WIDGET *wdg, void *data);
};

/////////////
// Actions //
/////////////


// Qt signals and slots //

#include <QPushButton>
#include <QScrollBar>
#include <QCheckBox>
#include <QRadioButton>
#include <QTimer>

class QIButton;
class QIScrollbar;
class QICheckButton;
class QIRadioButton;
class QITiming;

typedef const char * QT_SIGNL;

class QActionWidgetMapper {

private:
	QT_SIGNL signl;
	
public:
	QActionWidgetMapper(QT_SIGNL s)  : signl(s) { }

	QActionWidgetMapper() { }

	QT_SIGNL getSignal() const {
		return signl;
	}
};

class QIActionHandle {
public:
	IAction::Action action;
	IActionHandler *ah;
};

// Mapping IActions to Qt signals
class QIActionSignalMap : public IMapping< IAction::Action, QActionWidgetMapper > {
public:
	
	QIActionSignalMap();
};


#include <typeinfo>
#include <cstring>

class QIAction : public QObject,
		 public IAction,
		 public IMapping<QObject*, IActionHandler*>
{

	// Hacky rtti macros used for registerAction() to identify Qt object

#define tid(OBJ) \
	typeid(OBJ).name()

#define qtconnect(QTPTR, IACTION) \
	QObject::connect(dynamic_cast<QTPTR>(&wg), signal_map[IACTION].getSignal(), \
			 this, SLOT(generic_slot()))

#define add_handler(QTPTR) \
	mapping[(dynamic_cast<QTPTR>(&wg))] = ah;

	
	// FIXME: Should be done using dynamic_cast for multi dispatching. S.W.
#define ocmp_and_connect(IOBJ, QIPTR, QTPTR) \
	(!strcmp(tid(IOBJ), &tid(QIPTR)[1])) \
	do {\
		qtconnect(QTPTR, action);\
		add_handler(QTPTR);\
	} while(0)

	Q_OBJECT

private:
	QIActionSignalMap signal_map;

public slots:

	void generic_slot()
	{
	// FIXME: Only one specific signal from one object possible so far.
	//        No checks are done for which signal is received.

#ifdef DEBUG
	std::cerr << "[generic slot called]" << std::endl;
#endif
	
	for(std::map< QObject*, IActionHandler* >::const_iterator it = mapping.begin();
	    it != mapping.end(); ++it)
		if((QObject*)sender() == it->first)
			(*((IActionHandler*)it->second)).handler(dynamic_cast<IWidget&>(*it->first));

	}

public:
	
	void registerAction(IWidget &actionReceiver, IAction::Action action, IActionHandler *ah);

	
};

// TNW++ callbacks //

struct TObjectActionHandle
{
	IActionHandler *ah;
	IWidget *sender;

	TObjectActionHandle(IActionHandler *a, IWidget *w) : ah(a), sender(w) { }
	TObjectActionHandle() { }
};

extern "C" void tnw_callback(TN_WIDGET *caller, void*);

// Map IActions to TNW callback events
class TIActionEventMap : public IMapping<IAction::Action, USER_EVENT>
{

public:
	TIActionEventMap();
};

class TIAction : public IAction
{

public:
	TIActionEventMap event_map;

	// Mapping calling back widgets to IActionHandlers
	static std::map<TN_WIDGET*, TObjectActionHandle> action_handler_map;

	// Since we can't pass non-static member function pointers to tnRegisterCallBack, we
	// use this function that has C-linkage.
	//static void (*generic_callback)(TN_WIDGET *caller, void*);
	
public:
	
	void registerAction(IWidget &actionReceiver, IAction::Action action, IActionHandler *ah);
};


/////////////////
// Application //
/////////////////

#include <QWindowsStyle>

// Qt application
class QIApplication : public IApplication, private QApplication
{
private:
	QTimerActionHandler qtah;
	QIAction action;

public:
 	QIApplication(int &ac, char **av) : QApplication(ac, av)
	{
		setStyle(new QWindowsStyle);
	}
	
	int exec() {
		return QCoreApplication::exec();
	}

	void registerAction(IWidget &actionReceiver, IAction::Action a, IActionHandler *ah) {
		action.registerAction(actionReceiver, a, ah);
	}

	void registerTimer(ITimerHandler *timerh, IWidget *parent, time_t interval);
};

// TNW++ application
class TIApplication : public IApplication, public CApplication {

private:
	int argc;
	char ** argv;
	TIAction action;

public:
	
	TIApplication(int ac, char **av) : argc(ac), argv(av) { }

	int getArgc() const {
		return argc;
	}

	char** getArgv() const {
		return argv;
	}		

	int exec() {
		return CApplication::Init(argc, (char**)argv);
	}

	void registerAction(IWidget &actionReceiver, IAction::Action a, IActionHandler *ah) {
		action.registerAction(actionReceiver, a, ah);
	}
	
	void registerTimer(ITimerHandler *timerh, IWidget *parent, time_t interval) {

#if 0
		TN_WIDGET *wg = **dynamic_cast<TIWidget*>(parent);

		GR_WINDOW_ID wid = wg->wid;
		GR_WINDOW_INFO winf;

		GrGetWindowInfo(wid, &winf);
		GrSelectEvents(wid, winf.eventmask | GR_EVENT_MASK_TIMER);
		GrCreateTimer(wid, interval);	
#endif
		
		RegisterTimer(&TITimer::generic_callback, **this, timerh, interval);
	}

	TN_WIDGET* appParent() {
		//return m_pAppWidget;
		return NULL;
	}

	TN_WIDGET* operator*() {
		return *this;
	}
};

#endif
